/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sirtet.gui;

import java.awt.Dimension;
import sirtet.*;
import sirtet.event.*;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/**
 * An abstract parent for all visualizer of GameBoard objects
 * @author guangwei.zhu
 */
public abstract class GameBoardPanel extends JPanel implements BoardListener {

    /**
     * Class constructor
     * @param board the underlying GameBoard object
     */
    public GameBoardPanel(GameBoard board) {
        // Set the underlying board
        this.board = board;
        board.addBoardListener(this);
    }

    protected abstract void renderBlock(Graphics g, Object block, Point point, Dimension size);

    /**
     * Calling this method is necessary for this object to be garbage-collected.
     * This object should never be used after calling this method.
     */
    public void dispose() {
        board.removeBoardListener(this);
    }

    /**
     * Return the underlying GameBoard object.
     * @return the GameBoard object which is bound to the panel
     */
    public GameBoard getBoard() {
        return this.board;
    }

    /* Event handlers (to be overriden) */
    public void boardChanged(BoardEvent evt) {
        this.repaint();
    }

    public void boardGameOver(BoardEvent evt) {
    }

    public void boardStarted(BoardEvent evt) {
    }

    public void boardReset(BoardEvent evt) {
    }

    public void boardPieceFixed(BoardPieceFixedEvent evt) {
    }

    public void boardNextPieceChanged(BoardPieceEvent evt) {
    }

    public void boardBlocksEliminated(BoardBlocksEliminatedEvent evt) {
    }

    public void boardActionTriggered(BoardActionEvent evt) {
        this.repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        Point origin = this.getOrigin();
        Dimension blockSize = this.getBlockSize();

        int i, j;
        Point pos = new Point();

        // Draw fixed blocks
        for (i = 0; i < this.board.getHeight(); i++) {
            for (j = 0; j < this.board.getWidth(); j++) {
                pos.x = j * blockSize.width + origin.x;
                pos.y = i * blockSize.height + origin.y;
                renderBlock(g, this.board.getBlock(j, i), pos, blockSize);
            }
        }
    }

    protected Dimension getBlockSize() {
        Dimension blockSize = new Dimension();

        // By default the size is dynamically determined
        blockSize.width = this.getWidth() / board.getWidth();
        blockSize.height = this.getHeight() / board.getHeight();

        return blockSize;
    }

    protected Point getOrigin() {
        Point origin = new Point();
        Dimension blockSize = this.getBlockSize();

        switch (horizontalAlignment) {
            case SwingConstants.LEFT:
                origin.x = 0;
                break;
            case SwingConstants.CENTER:
                origin.x = (this.getWidth() - blockSize.width * board.getWidth()) / 2;
                break;
            case SwingConstants.RIGHT:
                origin.x = this.getWidth() - blockSize.width * board.getWidth();
                break;
        }

        switch (verticalAlignment) {
            case SwingConstants.TOP:
                origin.y = 0;
                break;
            case SwingConstants.CENTER:
                origin.y = (this.getHeight() - blockSize.height * board.getHeight()) / 2;
                break;
            case SwingConstants.BOTTOM:
                origin.y = this.getHeight() - blockSize.height * board.getHeight();
                break;
        }

        return origin;
    }

    public void setHorizontalAlignment(int alignment) {
        switch (alignment) {
            case SwingConstants.LEFT:
            case SwingConstants.CENTER:
            case SwingConstants.RIGHT:
                horizontalAlignment = alignment;
                break;
            default:
                throw new IllegalArgumentException();
        }

        this.repaint();
    }

    public void setVerticalAlignment(int alignment) {
        switch (alignment) {
            case SwingConstants.TOP:
            case SwingConstants.CENTER:
            case SwingConstants.BOTTOM:
                verticalAlignment = alignment;
                break;
            default:
                throw new IllegalArgumentException();
        }

        this.repaint();
    }
    protected int horizontalAlignment = SwingConstants.LEFT;
    protected int verticalAlignment = SwingConstants.TOP;
    protected GameBoard board;
}
